#ifndef __pe_object__
#define __pe_object__

#include <algorithm>
#include <string>
#include "vmface.h"
#include "pgtx64.h"

namespace xm
{
	class process_t;
	struct vmarea_t;
	//static size_t sc_to_prot( size_t ch )
	//{
	//	size_t prot = PAGE_NOACCESS;
	//	if( ch & IMAGE_SCN_MEM_READ )
	//		prot = PAGE_READONLY;
	//	if( ch & IMAGE_SCN_MEM_WRITE )
	//		prot = PAGE_READWRITE;
	//	if( ch & IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_CNT_CODE )
	//		prot <<= 4;
	//	return prot;
	//}
	static uint32_t sc_to_paga( uint32_t ch )
	{
		uint32_t paga = 0;
		if( ch & IMAGE_SCN_MEM_READ )
			paga |= PGA_READ;
		if( ch & IMAGE_SCN_MEM_WRITE )
			paga |= PGA_WRITE;
		if( ch & (IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_CNT_CODE) )
			paga |= PGA_EXECUTE;
		if( 0 == (ch & IMAGE_SCN_MEM_SHARED) )
			paga |= PGA_WCOPY;
		return paga;
	}
	static void fix_ms_dllname( char * p )
	{
		size_t len = strlen(p);
		if( !len ) return ;
		char * hp = p;
		while( isspace(*hp) ) ++ hp;
		char * tp = p + len - 1;
		while( tp > hp && isspace(*tp) ) -- tp;
		len = tp - hp + 1;
		if( len ) strncpy( p, hp, len );
		p[len] = 0;
		strlwr( p );
	}

	struct StreamNotImpl : IKBStream
	{
		STDMETHOD_(BOOL,IsVirtual)() { return TRUE; };
		STDMETHOD(Read)( LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_NOTIMPL; }
		STDMETHOD(Write)( LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_NOTIMPL; }
		STDMETHOD(Seek)( INT64 liDistance, SEEK_FORM emFrom ) { return E_NOTIMPL; }
		STDMETHOD(Tell)( UINT64 * puliPointer ) { return E_NOTIMPL; }
		STDMETHOD(Readp)( UINT64 uliPos, LPVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_NOTIMPL; }
		STDMETHOD(Writep)( UINT64 uliPos, LPCVOID lpBuffer, SIZE_T cbData, SIZE_T * lpEff ) { return E_NOTIMPL; }
		STDMETHOD(SetPos)( UINT64 uliPos ) { return E_NOTIMPL; }
		STDMETHOD_(UINT64,Length)() { return LENGTH_ERROR; }
		STDMETHOD(Truncate)() { return E_NOTIMPL; }
	};

	class imagex_t : public KernelObject<KoImage>, public StreamNotImpl, UnkBase
	{
	public:
		UNKNOWN_IMP2_( IKernelObject, IKBStream );
	public:
		iptr<IKBStream>			_file;
		size_t					_is64;
		IMAGE_FILE_HEADER		_file_hdr;
		union {
			IMAGE_OPTIONAL_HEADER32 _nt_hdr32;
			IMAGE_OPTIONAL_HEADER64 _nt_hdr64;
		};
		// 0 -> pe header
		std::vector<IMAGE_SECTION_HEADER>	_sections;

		imagex_t();

		virtual long open( IUnknown * file );

		bool_t		is_dll() 
		{ 
			return _file_hdr.Characteristics & WINPE_IMAGE_FILE_DLL;
		};

		bool_t		is_native()
		{
			return _is64 
				? _nt_hdr64.Subsystem == IMAGE_SUBSYSTEM_NATIVE 
				: _nt_hdr32.Subsystem == IMAGE_SUBSYSTEM_NATIVE;
		}

		IKBStream*	filep(){ return _file; };
		uint64_t	image_base();
		size_t		tls_rva();
		size_t		entry_point();
		size_t		num_of_dde();
		IMAGE_DATA_DIRECTORY * get_dde( );
		IMAGE_DATA_DIRECTORY * get_dde( size_t i );
		size_t		relocatable();
		IMAGE_SECTION_HEADER * get_section( size_t i );
		IMAGE_SECTION_HEADER * find_section( uint32_t rva, size_t * pi );
		
		STDMETHOD_(BOOL,IsVirtual)();
		STDMETHOD(Readp)( uint64_t rva, void * pbuf, size_t want, size_t * eff = 0 );
		STDMETHOD_(UINT64,Length)() ;

		size_t			num_of_segment();
		long			query_segment( size_t i, seginfo_t * seg );
		long			find_segment( uint32_t rva, seginfo_t * vmip );
		uint64_t		get_image_base();
		long			relocate( vmarea_t * area, process_t * fixer );
		long			resolve_imports( vmarea_t * area, process_t * fixer );

		enum { MX_THUNKS_TO_PTOC = 5000 };

		template< class IMAGE_THUNK_DATA3264, class PTRT >
		long resolve_thunks( vmarea_t * area, const char * libname, IMAGE_IMPORT_DESCRIPTOR * piidesc, process_t * fixer );

		uint32_t		get_proc_address_by_name( const char * name );
		
		uint32_t		get_proc_address_by_ord( size_t ord );

		uint32_t		get_proc_address_by_idx( size_t idx );

		const char *	get_proc_name_by_idx( size_t idx );

		const char * imagex_t::enum_named_proc( size_t idx, size_t * ord );

		uint32_t		get_export_ord_base() { return export_ord_base; }

	protected:

		typedef struct name_ord_pair
		{
			std::string	name;
			uint16_t	fidx;
		} name_ord_pair;

		typedef struct ord_to_name_pair
		{
			uint32_t	fidx;
			uint32_t	nidx;

			bool operator < ( const ord_to_name_pair & right ) const 
			{
				return fidx < right.fidx;
			}

		} o2n_t;

		static bool less_name_ord_pair( const name_ord_pair & left, const name_ord_pair & right )
		{
			return left.name < right.name;
		}

		size_t						export_loads;
		std::vector<uint32_t>		exports;
		typedef std::vector<name_ord_pair>	name_ord_vector;
		name_ord_vector				export_names;
		size_t						export_ord_base;
		std::vector<o2n_t>			export_names_o2n;

		long		load_exports();
	};
};


#endif